package com.jhf.youke.files.utils;


import com.jhf.youke.core.utils.StringUtils;
import com.jhf.youke.files.domain.exception.FilesException;
import com.jhf.youke.files.domain.service.FilesService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * mp4转换m3u8工具类
 * @author: rhj
 * @create: 2021-01-26 14:19
 **/
@Slf4j
@Component
public class ConvertM3U8 {

	@Resource
	private FilesService filesService;

	@Value("${ffmPegPath}")
	private String ffmPegPath;



	/**
	 * 验证上传文件后缀
	 *
	 * @param type 类型
	 * @return 是否为mp4
	 */

	private boolean validFileType(String type) {
		return "mp4".equals(type);
	}



	/**
	 * 将表单提交的MultipartFile上传到OSS中
	 * 实现思路：
	 * 先将文件本地上传
	 * 重命名，生成对应文件夹
	 * 调用ffmpeg切片转成m3u8
	 * 将mp4视频和转换后的m3u8以及ts放在一起，然后遍历文件目录
	 * 将文件上传后，删除本地文件夹和文件在将转换后文件上传到oss上     *
	 *
	 * @param file 上传的文件
	 * @return ResponseResult 结果
	 */
	public String convertM3U8(MultipartFile file) {
		String path = "";
		File targetFile = null;
		String originalFilename = file.getOriginalFilename();
		//获取文件后缀
		String suffix = StringUtils.substringAfter(originalFilename, ".");
		if(!validFileType(suffix)){
			throw new FilesException("视频格式不是mp4");
		}

		try {
			//获取文件名
			String filename = StringUtils.substringBefore(originalFilename, ".");
            String uploadDir = "video/" + UUID.randomUUID();
			targetFile = new File(uploadDir + "/" + filename + "." + suffix);
			FileUtils.writeByteArrayToFile(targetFile, file.getBytes());
			path = targetFile.getAbsolutePath();
			// 对视频进行切片
			processM3U8(path);
			// 拿到视频目录
			String targetDir = targetFile.getParent();
			// 原始视频删除
			targetFile.delete();
			// 从目录读取视频
            log.info("targetDir {}", targetDir);
			File files =  new File(targetDir);

			for (File f : files.listFiles()) {
				// 所有视频切片上传
				log.info("file {}", f.getName());
				String url = filesService.upload(uploadDir, getMultipartFile(f), true);
				log.info("url {}", url);
				if ("m3u8".equals(StringUtils.substringAfter(url, "."))) {
					path = url;
					log.info("path {}", path);
				}
			}

		} catch (Exception e) {
			log.error("上传异常 {}", e.getMessage());
		} finally {
			try {
				if (targetFile != null) {
					targetFile.delete();
				}
			} catch (Exception e) {

			}
		}
		log.info("last path {}", path);
		return path;
	}


	public  MultipartFile getMultipartFile(File file) {
		FileItem item = new DiskFileItemFactory().createItem(file.getName()
				, MediaType.MULTIPART_FORM_DATA_VALUE
				, true
				, file.getName());
		try (InputStream input = new FileInputStream(file);
			 OutputStream os = item.getOutputStream()) {
			// 流转移
			IOUtils.copy(input, os);
		} catch (Exception e) {
			throw new IllegalArgumentException("Invalid file: " + e, e);
		}

		return new CommonsMultipartFile(item);
	}

	/**
	 * ffmPeg程序转换m3u8
	 * ffmPeg -i vue.mp4 -c:v libx264 -hls_time 20 -hls_list_size 0 -c:a aac -strict -2 -f hls vue.m3u8
	 * ffmPeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
	 *
	 * @param folderUrl 文件路径
	 * @return 是否成功
	 */
	public boolean processM3U8(String folderUrl) {
		//这里就写入执行语句就可以了
		List<String> commend = new ArrayList<>();
		commend.add(ffmPegPath);
		commend.add("-i");
		commend.add(folderUrl);
		commend.add("-c:v");
		commend.add("libx264");
		commend.add("-hls_time");
		commend.add("10");
		commend.add("-hls_list_size");
		commend.add("0");
		commend.add("-c:a");
		commend.add("aac");
		commend.add("-strict");
		commend.add("-2");
		commend.add("-f");
		commend.add("hls");
		commend.add(StringUtils.substringBefore(folderUrl, ".") + ".m3u8");
		try {
			ProcessBuilder builder = new ProcessBuilder();
			builder.command(commend);
			Process p = builder.start();
			int i = doWaitFor(p);
			log.info("***i=【{}】***", i);
			p.destroy();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 监听ffmPeg运行过程
	 *
	 * @param p 进程
	 * @return 直接结果
	 */
	public int doWaitFor(Process p) {
		InputStream in = null;
		InputStream err = null;
		// returned to caller when p is finished
		int exitValue = -1;
		try {
			log.info("***检测ffmPeg运行***");
			in = p.getInputStream();
			err = p.getErrorStream();
			// Set to true when p is finished
			boolean finished = false;

			while (!finished) {
				try {
					while (in.available() > 0) {
						Character c = (char) in.read();
						System.out.print(c);
					}
					while (err.available() > 0) {
						Character c = (char) err.read();
						System.out.print(c);
					}

					exitValue = p.exitValue();
					finished = true;

				} catch (IllegalThreadStateException e) {
					Thread.sleep(500);
				}
			}
		} catch (Exception e) {
			log.error("doWaitFor();: unexpected exception - "
					+ e.getMessage());
		} finally {
			try {
				if (in != null) {
					in.close();
				}

			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
			if (err != null) {
				try {
					err.close();
				} catch (IOException e) {
					log.error(e.getMessage());
				}
			}
		}
		return exitValue;
	}

}


